package cgl.sensorgrid.sopac.gps.filters;

import java.util.Collections;
import java.util.List;
import java.util.LinkedList;
import cgl.narada.event.NBEvent;
import cgl.sensorgrid.sopac.gps.threadpool.ThreadPool;
import cgl.sensorgrid.filters.Filter;
import java.util.Calendar;
import cgl.sensorgrid.common.ITRFtoLatLon;
import java.nio.ByteOrder;
import cgl.narada.util.ByteUtilities;
import java.text.SimpleDateFormat;
import java.nio.ByteBuffer;

class ryoTOposFilter {
    private Object syncObj1 = new Object();
    private List eventList = Collections.synchronizedList(new LinkedList());
    private NBEvent ryo;
    private String header = "";
    private String position = "";
    private String xyzVariance = "";
    private String troposphericCovariance = "";
    private String satelliteInfo = "";
    private String RYOmessage = "";

    int[] a = new int[1];
    private ryoTOpos publisher;

    public ryoTOposFilter(ryoTOpos publisher_) {
        this.publisher = publisher_;
    }

    public void addEvent(NBEvent event) {
        synchronized (syncObj1) {
            eventList.add(event);
            if (eventList.size() == 1) {
                syncObj1.notify();
            }
        }
    }

    public void run() {
        boolean running = true;
        try {
            while (running) {
                synchronized (syncObj1) {
                    if (eventList.size() == 0) {
                        syncObj1.wait();
                    }
                    byte[] data2 = ((NBEvent) eventList.remove(0)).
                                   getContentPayload();
                    if (data2 != null && data2.length > 0) {
                        processRawMessage(data2);
                        //System.out.println(new String(data2));
                    }
                }
            }
            return;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }


    /**
     *
     * @param input byte[]
     */
    public void processRawMessage(byte[] input) {
        try {
            boolean validHeader = false;
            if (input.length >= 5) {
                validHeader = decodeHeader(input);
            }
            if (validHeader && input.length > 52) {
                decodePosition(input, 5);
            }

            String pos = this.getPosition();
            if (pos.endsWith("\n")) {
                pos = pos.substring(0, pos.length() - 1);
            }
            if (pos != null && pos.trim() != "" && pos.trim().length() > 0) {
                publisher.publishData((pos + "\nEOF\n").getBytes());
                 //System.out.println("\n" + pos + "\nEOF");
            }
            this.position = "";

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     *
     * @param message String
     * @return String
     */
    public String messageToGML(String message) {
        try {
//            MakeSopacObservationCollection gml = new
//                                                 MakeSopacObservationCollection();
//            return gml.makeSOPACObservationCollection(message);
            return "";
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }


    /**
     *
     * @param input byte[]
     * @return boolean
     */
    public boolean decodeHeader(byte[] input) {
        try {
            boolean validHeader = false;
            cgl.narada.util.ByteUtilities b = new cgl.narada.util.ByteUtilities(
                    a);

            int SyncMarker1 = b.getIntFromByte(input[0]);
            int SyncMarker2 = b.getIntFromByte(input[1]);
            if (Integer.toHexString(SyncMarker1).toUpperCase().equalsIgnoreCase(
                    "9C") &&
                Integer.toHexString(SyncMarker2).toUpperCase().equals("A5")) {
                validHeader = true;
            }
            return validHeader;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    /**
     *
     * @param input byte[]
     * @param offset int
     */
    public String decodePosition(byte[] input, int offset) {
        try {
            String line = "";
            ByteUtilities b = new ByteUtilities(a);
            int i = offset;
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

            //Date
            short GPSWeek = bb.getShort(i);
            int GPSmsOfWeek = bb.getInt(i + 2);
            String date = getDate(Short.toString(GPSWeek),
                                  Integer.toString(GPSmsOfWeek));
            //Site info
            int SiteIndex = b.getIntFromByte(input[i + 6]);
            int SiteCount = b.getIntFromByte(input[i + 7]);
            String SiteId = Character.toString((char) (input[i + 8])) +
                            Character.toString((char) (input[i + 9])) +
                            Character.toString((char) (input[i + 10])) +
                            Character.toString((char) (input[i + 11]));

            //Skip 12 and 13
            //            line += " " + GPSWeek;
            //            line += " " + GPSmsOfWeek;
            line += "\t" + SiteId;
//         line += "\t" + SiteIndex;
//         line += "\t" + SiteCount;

            line += "\t" + date;

            //ITRF Positions
            double X = bb.getDouble((i + 14));
            double Y = bb.getDouble((i + 22));
            double Z = bb.getDouble((i + 30));
            double T = bb.getDouble((i + 38));
            line += "\t" + X;
            line += "\t" + Y;
            line += "\t" + Z;
            line += "\t" + T;
            //            System.out.println("X " + X);
            //            System.out.println("Y " + Y);
            //            System.out.println("Z " + Z);
            //            System.out.println("T " + T);


            //Lat, Lon, Height positions
            double lat = 0.0;
            double lon = 0.0;
            double hi = 0.0;

            ITRFtoLatLon latlon = new ITRFtoLatLon();
            double[] llh = null;
            llh = latlon.GetLatLonHeightRadians(X, Y, Z);

            if (llh != null && llh.length == 3) {
                line += "\t" + llh[0];
                line += "\t" + llh[1];
                line += "\t" + llh[2];
            }

            if (input.length > i + 47) {
                String flagString = b.printByte(input[i + 47]);
                boolean xyz = false;
                boolean tropo = false;
                boolean satellite = false;

                if (!flagString.equals("") && flagString.length() == 8) {
                    if (flagString.charAt(7) == '1') {
                        //                    line += " XYZ";
                        xyz = true;
                    }
                    if (flagString.charAt(6) == '1') {
                        //                    line += " Troposhpheric";
                        tropo = true;
                    }
                    if (flagString.charAt(5) == '1') {
                        //                    line += " Satellite";
                        satellite = true;
                    }
                }

                i += 47;

                if (input.length > 51) {
                    if (xyz == true) {
                        i = decodeXYZVariances(input, i + 1);
                    }
                    line += " " + this.getXyzVariance();
                    this.setXyzVariance("");
                }

                if (input.length > i) {
                    if (satellite == true) {
                        i = decodeSatellite(input, i + 1);
                    }
                    line += " " + this.getSatelliteInfo();
                    this.setSatelliteInfo("");
                }

                if (line != null && !line.equals("")) {
                    this.appendPosition(line.trim() + "\n");
                }

                if (input.length > i + 52) {
                    decodePosition(input, i + 7);
                }
            }
            return line;
        } catch (Exception ex) {
            System.err.println("\nException occured in decodePosition()");
            ex.printStackTrace();
            return null;
        }
    }


    /**
     *
     * @param input byte[]
     * @param i int
     * @return int
     */
    public int decodeXYZVariances(byte[] input, int i) {
        try {
            String line = "";
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

//            double scale = bb.getDouble(i);
//            double Xvar = bb.getDouble((i + 8));
//            double Yvar = bb.getDouble((i + 16));
//            double Zvar = bb.getDouble((i + 24));
//            double YXvar = bb.getDouble((i + 32));
//            double YZvar = bb.getDouble((i + 40));
//            double ZXvar = bb.getDouble((i + 48));

            i += 55;
//           line += " " + scale;
//            line += " " + Xvar;
//            line += " " + Yvar;
//            line += " " + Zvar;
//            line += " " + YXvar;
//            line += " " + YZvar;
//            line += " " + ZXvar;
//            this.setXyzVariance(line);
            return i;
        } catch (Exception ex) {
            //            System.err.println("\nException occured in decodeVariances()");
            return i;
        }

    }

    public int decodeTroposphericCoverianceBlock(byte[] input, int i) {
        try {
            String line = "";
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

//            double Tvar = bb.getDouble((i + 8));
//            double TXcovar = bb.getDouble((i + 16));
//            double TYcovar = bb.getDouble((i + 24));
//            double TZcovar = bb.getDouble((i + 32));

            i += 33;
//            line += " " + Tvar;
//            line += " " + TXcovar;
//            line += " " + TYcovar;
//            line += " " + TZcovar;
            this.setTroposphericCovariance(line);
            return i;
        } catch (Exception ex) {
            //            System.err.println("\nException occured in decodeVariances()");
            return i;
        }

    }

    public int decodeSatellite(byte[] input, int i) {
        try {
            String line = "";
            ByteUtilities b = new ByteUtilities(a);
            ByteBuffer bb = ByteBuffer.wrap(input);
            bb.order(ByteOrder.LITTLE_ENDIAN);

            int satelliteCount = b.getIntFromByte(input[i]);
            // line += " " + satelliteCount;
            double GDOP = bb.getDouble((i + 1));
            // line += " " + GDOP;

            if (satelliteCount > 0) {
                i += 9;
                for (int j = 0; j < satelliteCount; j++) {
                    if (input.length > i + 5) {
//                        int PRN = b.getIntFromByte(input[i]);
//                        int PRNFlags = b.getIntFromByte(input[i + 1]);
//                        short Elevation = bb.getShort(i + 2);
//                        short Azimuth = bb.getShort(i + 4);
//                        line += " " + (j + 1);
//                        line += " " + PRN;
//                        line += " " + PRNFlags;
//                        line += " " + Elevation;
//                        line += " " + Azimuth;
                        i += 6;
                    }
                }
                this.setSatelliteInfo(line);
            }
            return i;
        } catch (Exception ex) {
            //            System.err.println("\nException occured in decodeSatellite()");
            //            ex.printStackTrace();
            return i;
        }
    }


    public void setHeader(String header) {
        this.header = header;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    public void appendPosition(String pos) {
        this.position += pos;
    }


    public void setXyzVariance(String xyzVariance) {
        this.xyzVariance = xyzVariance;
    }

    public void setTroposphericCovariance(String troposphericCovariance) {
        this.troposphericCovariance = troposphericCovariance;
    }

    public void setSatelliteInfo(String satelliteInfo) {
        this.satelliteInfo = satelliteInfo;
    }

    public void setRYOmessage(String header, String position) {
        String RYOmsg = header;
        RYOmsg.trim();

        RYOmsg += position;
        RYOmsg.trim();
        this.RYOmessage = RYOmsg;
    }

    public String getHeader() {
        return header;
    }

    public String getPosition() {
        return position;
    }

    public String getXyzVariance() {
        return xyzVariance;
    }

    public String getTroposphericCovariance() {
        return troposphericCovariance;
    }

    public String getSatelliteInfo() {
        return satelliteInfo;
    }

    public String getRYOmessage() {
        return RYOmessage;
    }

    /**
     * Calculates Gregorian Date from GPS Week and GPS milliseconds
     * @param GPS_week String
     * @param GPS_millisecond String
     * @return String
     */
    public String getDate(String GPS_week, String GPS_millisecond) {
        try {
            //GPS_epoch = "1/6/1980";
            long GPS_start_epoch = 315964800;
            long GPS_leapseconds = 13;

            long time = GPS_start_epoch +
                        Long.parseLong(GPS_week) * 7 * 24 * 3600 +
                        Long.parseLong(GPS_millisecond) / 1000 -
                        GPS_leapseconds;

            Calendar cal = Calendar.getInstance();
            cal.setTimeInMillis(time * 1000);
            SimpleDateFormat myDateFormat =
                    new SimpleDateFormat("yyyy-MM-dd\thh:mm:ssa-z");

            return myDateFormat.format(cal.getTime());
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

}


/**
 *
 * <p>Title: SensorGrid</p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2006</p>
 *
 * <p>Company: </p>
 *
 * @author Galip Aydin
 * @version 1.0
 */
public class ryoTOpos extends Filter {

    private static ThreadPool pool = new ThreadPool(10);
    private ryoTOposFilter r2a;

    public ryoTOpos() {
    }

    public ryoTOpos(String[] args, String[] filterSpecificArgs) {
        if (args == null || args.length == 0) {
            System.err.println(
                    "Incorrect number of arguments in ryo2ascii::Constructor...");
        }
        else {
            this.initialize(args);
            r2a = new ryoTOposFilter(this);
            r2a.run();
        }
    }

    public void onEvent(NBEvent nbEvent) {
        if (nbEvent.getContentPayload() != null) {
            if (r2a != null && nbEvent != null) {
                r2a.addEvent(nbEvent);
            }
        }
    }


    public static void main(String args[]) {
        if (args.length < 5) {
            System.out.println("Usage: java cgl.sensorgrid.sopac.gps.filters.ryo2ascii commType nbHost nbPort subTopic pubTopic");
            System.exit(0);
        }
        String commType = args[0];
        String nbHost = args[1];
        String nbPort = args[2];
        String subTopic = args[3];
        String pubTopic = args[4];
        String[] argsa = {commType,
                         nbHost,
                         nbPort,
                         subTopic,
                         pubTopic};
//
//        String[] argsa = {"tcp",
//                         "gf2.ucs.indiana.edu",
//                         "5544",
//                         "/SOPAC/GPS/CRTN_01/RYO",
//                         "/SOPAC/GPS/CRTN_01/POS"};

        ryoTOpos test = new ryoTOpos(argsa, null);
    }
}
